﻿using Serilog;
using Serilog.Debugging;
using Serilog.Events;
using Serilog.Formatting.Json;
using Serilog.Sinks.Elasticsearch;
using Serilog.Sinks.File;
using Serilog.Sinks.SystemConsole.Themes;
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;

namespace Sustainalytics.Utils.Serilog
{
    public class CommonEventSerilog
    {
        public static Lazy<CommonEventSerilog> _lazyLog = new Lazy<CommonEventSerilog>(InitLog, LazyThreadSafetyMode.ExecutionAndPublication);
        private readonly ILogger _logger;


        public static CommonEventSerilog Log
        {
            get
            {
                return _lazyLog.Value;
            }
        }

        public static CommonEventSerilog InitLog()
        {
            var log = new CommonEventSerilog();
            return log;

        }

        public CommonEventSerilog()
        {
            var loggerConfig = new LoggerConfiguration()
                  .MinimumLevel.Debug()
                  .WriteTo.Console(theme: SystemConsoleTheme.Literate)
                  .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200")) // for the docker-compose implementation
                  {
                      IndexFormat = "testpi-{0:yyyy.MM.dd}",
                      AutoRegisterTemplate = true,
                      RegisterTemplateFailure = RegisterTemplateRecovery.IndexAnyway,
                      FailureCallback = e => Console.WriteLine("Unable to submit event " + e.MessageTemplate),
                      EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog |
                                         EmitEventFailureHandling.WriteToFailureSink |
                                         EmitEventFailureHandling.RaiseCallback,
                      FailureSink = new FileSink("../failuresCommonEventSerilog-" + DateTime.Now.Ticks + ".txt", new JsonFormatter(), null)
                  });
            _logger = loggerConfig.CreateLogger();

            SelfLog.Enable(Console.Error);
        }

        public void Exception(Exception ex)
        {
            if (ex != null)
                this.Exception(ex.GetType().ToString(), ex.Message, ex.ToString());
        }

        public void Exception(string exceptionType, string exceptionMessage, string exceptionInformation)
        {
            var eventId = 2;
            if (_logger.IsEnabled(LogEventLevel.Error))
                _logger.Error($"Exception {eventId}, {exceptionType}, {exceptionMessage}, {exceptionInformation}");
        }

        public void ApplicationStartError(Exception ex)
        {
            var eventId = 3;
            string formatedmessage = String.Format($"ApplicationStartError  {eventId}, {ex.GetType().ToString()}, {ex.Message}");

            if (_logger.IsEnabled(LogEventLevel.Error))
                _logger.Error(formatedmessage);
        }

        public void ApplicationStopError(Exception ex)
        {
            var eventId = 4;
            string formatedmessage = String.Format($"ApplicationStopError  {eventId}, {ex.GetType().ToString()}, {ex.Message}");

            if (_logger.IsEnabled(LogEventLevel.Error))
                _logger.Error(formatedmessage);
        }

        public void OnUnhandledDomainException(Exception ex)
        {
            var eventId = 5;
            string formatedmessage = String.Format($"UnhandledDomainException  {eventId}, {ex.GetType().ToString()}, {ex.Message}");

            if (_logger.IsEnabled(LogEventLevel.Error))
                _logger.Error(formatedmessage);
        }

        public void LogWebRequestSuccess(string methName, string requestUri, string ctrlName, string args, string reasonPhrase)
        {
            var eventId = 6;
            var date = DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss");
            if (_logger.IsEnabled(LogEventLevel.Information))
            {
                _logger.Information($"WebRequestSuccess, EventId {eventId}, for {ctrlName}, httpMethod {methName}, requestUri {requestUri}, args {args}, reasonPhrase {reasonPhrase}, date {date}");
            }
        }

        public void LogWebRequestError(string methName, string requestUri, string ctrlName, string args, string reasonPhrase, string exceptionMessage)
        {
            var eventId = 7;
            var date = DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss");
            if (_logger.IsEnabled(LogEventLevel.Error))
            {
                _logger.Error($"WebRequestError, EventId {eventId}, for {ctrlName}, httpMethod {methName}, requestUri {requestUri}, args {args}, reasonPhrase {reasonPhrase}, exception {exceptionMessage}, date {date}");
            }
        }

        public void InOutEvent(string controllerName, string httpMethod, int statusCode, string reasonPhrase)
        {
            var eventId = 8;
            if (_logger.IsEnabled(LogEventLevel.Information))
            {
                _logger.Information($"InOutEvent {eventId}.Controller {controllerName}, httpMethod {httpMethod}, statusCode {statusCode}, reasonPhrase {reasonPhrase}");
            }
        }

       

        public void Test(string controllerName, string httpMethod, int statusCode, string reasonPhrase)
        {
            var eventId = 9;
            if (_logger.IsEnabled(LogEventLevel.Information))
            {
                _logger.Information($"Test {eventId}, {controllerName}, {httpMethod}, {statusCode}, {reasonPhrase}");
            }
        }

        public void ArgumentsEvent(string key, string value)
        {
            var eventId = 10;
            if (_logger.IsEnabled(LogEventLevel.Information))
            {
                _logger.Information($"ArgumensEvent {eventId}, {key}, {value}");
            }
        }

        public void OuptputEvent(string outputValue)
        {
            var eventId = 11;
            if (_logger.IsEnabled(LogEventLevel.Information))
            {
                _logger.Information($"OuptputEvent {eventId}, {outputValue}");
            }
        }

        public void MongoEvent(string className, string dbName, string collectionName, string opName, string args, string result)
        {
            var eventId = 12;
            if (_logger.IsEnabled(LogEventLevel.Information))
            {
                _logger.Information($"MongoEvent {eventId}, {className}, {dbName}, {collectionName}, {opName}, {args}, {result}");
            }
        }

        public void CRMReadAccountDirectlyLinkedClaims(string account, string claims)
        {
            var eventId = 13;
            if (_logger.IsEnabled(LogEventLevel.Information))
            {
                _logger.Information($"CRM Read Directly Linked Claims for account {account}");
            }
        }

        public void CRMReadAccountPackageLinkedClaims(string account, string claims)
        {
            var eventId = 14;
            if (_logger.IsEnabled(LogEventLevel.Information))
            {
                _logger.Information($"{eventId}, CRM Read Package Linked Claims for account {account}");
            }
        }

        public void ErrorWritingToRedis(string key, string value)
        {
            var eventId = 15;
            if (_logger.IsEnabled(LogEventLevel.Error))
            {
                _logger.Error($"{eventId}, Error writting to redis key {key}");
            }
        }

        public void ErrorRemoveFromRedis(string key)
        {
            var eventId = 16;
            if (_logger.IsEnabled(LogEventLevel.Error))
            {
                _logger.Error($"{eventId}, Error removing from redis key {key}");
            }
        }

        public void RepositoryEvent(string text)
        {
            var eventId = 17;
            if (_logger.IsEnabled(LogEventLevel.Information))
            {
                _logger.Information($"{eventId}, RepositoryEvent {text}");
            }
        }

        public void InitController([CallerFilePath]string callerFilePath = "")
        {
            var eventId = 18;
            var controllerName = Path.GetFileNameWithoutExtension(callerFilePath);
            if (_logger.IsEnabled(LogEventLevel.Verbose))
            {
                _logger.Verbose($"{eventId}, for InitController {controllerName}");
            }
        }

        public void InitControllerSuccess([CallerFilePath]string callerFilePath = "")
        {
            var eventId = 19;
            var controllerName = Path.GetFileNameWithoutExtension(callerFilePath);
            if (_logger.IsEnabled(LogEventLevel.Information))
            {
                _logger.Information($"{eventId}, for InitControllerSuccess {controllerName}");
            }
        }

        public void InitControllerError(Exception ex, [CallerFilePath]string callerFilePath = "")
        {
            var eventId = 20;
            var controllerName = Path.GetFileNameWithoutExtension(callerFilePath);
            if (_logger.IsEnabled(LogEventLevel.Error))
            {
                _logger.Error($"{eventId}, for InitControllerError {controllerName}, exception {ex.Message}");
            }
        }

        public void CallerName([CallerMemberName]string callerName = "")
        {
            var eventId = 21;
            if (_logger.IsEnabled(LogEventLevel.Verbose))
            {
                _logger.Verbose($"{eventId}, for Function {callerName}");
            }
        }

        public void CallerNameError(Exception ex, [CallerMemberName]string callerName = "")
        {
            var eventId = 22;
            if (_logger.IsEnabled(LogEventLevel.Error))
            {
                _logger.Error($"{eventId}, for Function {callerName}, exception {ex.Message}");
            }
        }
        
        public void CallerError(string message, Tuple<string, string, int> caller, params Object[] paramsList)
        {
            var eventId = 33;

            if (_logger.IsEnabled(LogEventLevel.Error))
            {
                var fileName = Path.GetFileNameWithoutExtension(caller.Item2);

                StringBuilder info = new StringBuilder();
                foreach (Object param in paramsList)
                {
                    info.Append(", " + param.ToString());
                }

                _logger.Error($"{eventId}, {message} for Function {caller.Item1}, fileName {fileName}, lineNumber {caller.Item3}{info}");
            }
        }

        public void Info(params Object[] paramsList)
        {
            var eventId = 40;

            if (_logger.IsEnabled(LogEventLevel.Information))
            {
                StringBuilder info = new StringBuilder();
                foreach (Object param in paramsList)
                {
                    info.Append(", " + param.ToString());
                }

                _logger.Information($"{eventId}{info}");
            }
        }
        
    }
}
